use std::fs::File;
use std::io::{BufRead, BufReader};
use std::collections::VecDeque;

fn parse_input() -> Vec<String> {
    let file = File::open("./day10/input.txt").unwrap();
    let reader = BufReader::new(file);
    reader.lines().map(|line| line.unwrap()).collect()
}

fn is_left(ch: char) -> bool {
    ch == '(' || ch == '[' || ch == '{' || ch == '<'
}

fn is_match(left: char, right: char) -> bool {
    match left {
        '(' => right == ')',
        '[' => right == ']',
        '{' => right == '}',
        '<' => right == '>',
        _   => panic!("Unkonw left")
    }
}

#[allow(unused)]
fn part1(lines: &Vec<String>) {

    fn syntax_error_score(chunks: &String) -> i32 {
        let mut score = 0;
        let mut expecets = VecDeque::new();

        for ch in chunks.chars() {
            if is_left(ch) {
                expecets.push_back(ch);
            } else {
                match expecets.pop_back() {
                    None => break,
                    Some(left_expect) => {
                        if !is_match(left_expect, ch) {
                            return match ch {
                                ')' => 3,
                                ']' => 57,
                                '}' => 1197,
                                '>' => 25137,
                                _   => panic!("")
                            };
                        }
                    }
                }
            }
        }
        
        0
    } 

    let result: i32 = lines.iter().map(|line| syntax_error_score(line)).sum();
    
    println!("Part1: {}", result);
}

#[allow(unused)]
fn part2(lines: &Vec<String>) {
    fn get_remind_lefts(chunks: &String) -> Result<VecDeque<char>, ()> {
        let mut score = 0;
        let mut expecets = VecDeque::new();

        for ch in chunks.chars() {
            if is_left(ch) {
                expecets.push_back(ch);
            } else {
                match expecets.pop_back() {
                    None => break,
                    Some(left_expect) => {
                        if !is_match(left_expect, ch) {
                            return Err(())
                        }
                    }
                }
            }
        }
        
        Ok(expecets)
    } 

    fn get_socre(remind_lefts: VecDeque<char>) -> i64 {
        fn char_to_score(ch: char) -> i64 {
            match ch {
                '(' => 1,
                '[' => 2,
                '{' => 3,
                '<' => 4,
                _   => panic!("")
            }
        }

        remind_lefts.into_iter().rev().fold(0, |acc, left| -> i64 {
            acc * 5 + char_to_score(left)
        })
    }

    let mut socres: Vec<i64> = 
        lines.into_iter().filter_map(|line| get_remind_lefts(line).ok())
                         .map(|remind_lefts| get_socre(remind_lefts))
                         .collect();
    socres.sort();

    let index = socres.len() / 2;
    println!("Part2: {}", socres[index]);

}

fn main() {
    let lines = parse_input();
    part1(&lines);
    part2(&lines);
}
